package xutil

/*
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

void createJobObject(int pid) {
	char jobname[32];
    snprintf(jobname, sizeof(jobname), "xneo_client_group_%u", GetCurrentProcessId());
    HANDLE hJob = CreateJobObject(NULL, (LPCSTR)jobname);
    if (hJob) {
        SetHandleInformation(hJob, HANDLE_FLAG_INHERIT, 0);
        JOBOBJECT_EXTENDED_LIMIT_INFORMATION ji;
        memset(&ji, 0, sizeof(ji));
        ji.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        if (SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &ji, sizeof(ji))) {
            HANDLE hPro = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
            if (AssignProcessToJobObject(hJob, hPro)) {
				printf("%s: %d\r\n", "成功关联作业对象", pid);
            } else {
				printf("%s: %d\r\n", "AssignProcessToJobObject", GetLastError());
            }
        } else {
			printf("%s: %d\r\n", "SetInformationJobObject", GetLastError());
        }
    } else {
		printf("%s: %d\r\n", "CreateJobObject", GetLastError());
    }
	fflush(stdout);
}

*/
import "C"

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"
)

func Restart(exe string, arg ...string) {
	cmd := exec.Command(exe, arg...)
	_, _, err := CmdStart(cmd)
	if err != nil {
		log.Println(err)
	}
}

func str2ptr(s string) *uint16 {
	u, _ := syscall.UTF16PtrFromString(s)
	return u
}

func AdminExec(se ShellExecute) error {
	//str2ptr := func(s string) *uint16 {
	//	u, _ := syscall.UTF16PtrFromString(s)
	//	return u
	//}
	
	shell32, err := syscall.LoadLibrary("Shell32.dll")
	if err != nil {
		return err
	}
	defer syscall.FreeLibrary(shell32)
	
	ShellExecuteW, err := syscall.GetProcAddress(shell32, "ShellExecuteW")
	if err != nil {
		return err
	}
	
	r, _, errno := syscall.Syscall6(uintptr(ShellExecuteW), 6,
		0,
		uintptr(unsafe.Pointer(str2ptr("runas"))),
		uintptr(unsafe.Pointer(str2ptr(se.FileName))),
		uintptr(unsafe.Pointer(str2ptr(se.Parameters))),
		uintptr(unsafe.Pointer(str2ptr(se.Directory))),
		uintptr(se.ShowCmd))
	
	if r != 42 {
		return errno
	}
	
	return nil
}

type UnregContextMenu struct {
	Submit []string `json:"submit"` //提交渲云渲染
}

var unregTemp = "unreg.json"

//卸载插件式提交的渲染软件里的插件
func UnregisterPluginMenu() {
	log.Println("un ", filepath.Join(AppDirPath(), "deleteMaxPlugin.cmd"))
	cmd := exec.Command(filepath.Join(AppDirPath(), "deleteMaxPlugin.cmd"))
	cmd.Env = os.Environ()
	log.Println(cmd.Env)
	_, _, err := CmdStart(cmd)
	log.Println("err", err)
	if err != nil {
		return
	}
	err = cmd.Wait()
	log.Println("wait err", err)
}

type ulong int32
type ulong_ptr uintptr

type PROCESSENTRY32 struct {
	dwSize              ulong
	cntUsage            ulong
	th32ProcessID       ulong
	th32DefaultHeapID   ulong_ptr
	th32ModuleID        ulong
	cntThreads          ulong
	th32ParentProcessID ulong
	pcPriClassBase      ulong
	dwFlags             ulong
	szExeFile           [260]byte
}

func SysProcessByPPID(parentPID int) (ret []ProcessStruct) {
	ss := SysProcessList()
	for _, s := range ss {
		if s.ParentProcessID == parentPID {
			ret = append(ret, s)
		}
	}
	return
}

func SysProcessList() (ret []ProcessStruct) {
	//进程快照
	kernel32 := syscall.NewLazyDLL("kernel32.dll")
	CreateToolhelp32Snapshot := kernel32.NewProc("CreateToolhelp32Snapshot")
	pHandle, _, _ := CreateToolhelp32Snapshot.Call(uintptr(0x2), uintptr(0x0))
	if int(pHandle) == -1 {
		log.Println("CreateToolhelp32Snapshot err")
		return
	}
	
	Process32Next := kernel32.NewProc("Process32Next")
	for {
		var proc PROCESSENTRY32
		proc.dwSize = ulong(unsafe.Sizeof(proc))
		if rt, _, _ := Process32Next.Call(uintptr(pHandle), uintptr(unsafe.Pointer(&proc))); int(rt) == 1 {
			
			len_szExeFile := 0
			for _, b := range proc.szExeFile {
				if b == 0 {
					break
				}
				len_szExeFile++
			}
			var bytetest []byte = []byte(proc.szExeFile[:len_szExeFile])
			ret = append(ret, ProcessStruct{
				ProcessName:     string(bytetest),
				ProcessID:       int(proc.th32ProcessID),
				ParentProcessID: int(proc.th32ParentProcessID),
			})
		} else {
			break
		}
	}
	
	CloseHandle := kernel32.NewProc("CloseHandle")
	_, _, _ = CloseHandle.Call(pHandle)
	
	//sort.Sort(ProcessStructSlice(ret))
	return
}

var (
	kernel32         = syscall.NewLazyDLL("kernel32.dll")
	procSetStdHandle = kernel32.NewProc("SetStdHandle")
)

// redirectStderr to the file passed in
func RedirectStderr(f *os.File) {
	err := setStdHandle(syscall.STD_ERROR_HANDLE, syscall.Handle(f.Fd()))
	if err != nil {
		log.Println("Failed to redirect stderr to file:", err)
	}
	// SetStdHandle does not affect prior references to stderr
	os.Stderr = f
}

func setStdHandle(stdhandle int32, handle syscall.Handle) error {
	r0, _, e1 := syscall.Syscall(procSetStdHandle.Addr(), 2, uintptr(stdhandle), uintptr(handle), 0)
	if r0 == 0 {
		if e1 != 0 {
			return error(e1)
		}
		return syscall.EINVAL
	}
	return nil
}

func FileRealPath(file string) string {
	for strings.Contains(file, "\\") {
		file = strings.ReplaceAll(file, "\\", "/")
	}
	for strings.Contains(file, "//") {
		file = strings.ReplaceAll(file, "//", "/")
	}
	
	var rets []string
	tmp := ""
	dirs := strings.Split(file, "/")
	
	for _, dir := range dirs {
		if dir == "" {
			continue
		}
		if tmp == "" {
			tmp = dir
		} else {
			tmp += "/" + dir
		}
		
		var find syscall.Win32finddata
		h, err := syscall.FindFirstFile(str2ptr(tmp), &find)
		if err == nil {
			syscall.FindClose(h)
			name := syscall.UTF16ToString(find.FileName[:])
			if strings.HasSuffix(strings.ToLower(tmp), strings.ToLower(name)) {
				rets = append(rets, name)
			} else {
				rets = append(rets, dir)
			}
		} else {
			//log.Println(dir, err)
			rets = append(rets, dir)
		}
	}
	resultName := strings.Join(rets, "/")
	if strings.Contains(resultName, ":") {
		return resultName
	} else {
		return "//" + resultName
	}
}

func CreateJobObject(pid int) {
	C.createJobObject(C.int(pid))
}
